#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <errno.h>

#include "sans.h"

#include "symboltable.h"
#include "stack.h"
#include "garbagecollector.h"
#include "runtime.h"

void test_stack_fold() {
  puts("# testing stack fold:");
  
  stack_push(SDSYMB("a"));
  stack_push(SDSYMB("b"));
  stack_push(SDSYMB("c"));
  stack_push(SDSYMB("d"));
  stack_push(SDSYMB("e"));
  stack_push(SDSYMB("f"));

  bltn_display(stack_ref(5));
  bltn_display(stack_ref(4));
  bltn_display(stack_ref(3));
  bltn_display(stack_ref(2));
  bltn_display(stack_ref(1));
  bltn_display(stack_ref(0));
  puts("");

  stack_fold(2, 3);

  bltn_display(stack_ref(2));
  bltn_display(stack_ref(1));
  bltn_display(stack_ref(0));
  puts("");

  puts("");
  puts("");
}

void test_symbol_table() {
  u foo, bar, baz, bar2;
  
  puts("# testing symbol table:");
  
  foo = intern("foo");
  bar = intern("bar");
  baz = intern("baz");
  bar2 = intern("bar");

  printf("foo: %s\n", lookup(foo));
  printf("bar: %s\n", lookup(bar));
  printf("baz: %s\n", lookup(baz));
  
  printf("bar = bar2: %s\n", bar == bar2 ? "yes" : "NO!");

  puts("");
  puts("");
}

void test_stack() {
  sdata n;
  
  puts("# testing stack:");
  
  stack_push((sdata){.tag = tnumb, .data.ivalue = 3});
  stack_push((sdata){.tag = tnumb, .data.ivalue = 2});
  stack_push((sdata){.tag = tnumb, .data.ivalue = 1});

  n = stack_pop();
  assert(n.tag == tnumb);
  printf("%ld\n", n.data.ivalue);
  n = stack_pop();
  assert(n.tag == tnumb);
  printf("%ld\n", n.data.ivalue);
  n = stack_pop();
  assert(n.tag == tnumb);
  printf("%ld\n", n.data.ivalue);

  puts("");
  puts("");
}

void test_cons() {
  sdata c, n;
  
  puts("# testing cons:");
  
  c = cons((sdata){.tag = tnumb, .data.ivalue = 11},
	   (sdata){.tag = tnumb, .data.ivalue = 22});
  
  c = cons(c,
	   (sdata){.tag = tnumb, .data.ivalue = 33});
  
  n = bltn_car(bltn_car(c));
  assert(n.tag == tnumb);
  printf("%ld\n", n.data.ivalue);
  
  n = bltn_cdr(bltn_car(c));
  assert(n.tag == tnumb);
  printf("%ld\n", n.data.ivalue);
  
  n = bltn_cdr(c);
  assert(n.tag == tnumb);
  printf("%ld\n", n.data.ivalue);
  
  puts("");
  puts("");
}

void test_cons2() {
  sdata c, n;
  
  puts("# testing cons 2:");
  
  c = cons((sdata){.tag = tnumb, .data.ivalue = 11},
	   (sdata){.tag = tnumb, .data.ivalue = 22});

  bltn_set_car(c, (sdata){.tag = tnumb, .data.ivalue = 44});
  bltn_set_cdr(c, c);
  
  n = bltn_car(c);
  assert(n.tag == tnumb);
  printf("%ld\n", n.data.ivalue);
  
  n = bltn_car(bltn_cdr(c));
  assert(n.tag == tnumb);
  printf("%ld\n", n.data.ivalue);
  
  puts("");
  puts("");
}

void test_vector() {
  sdata v, n;
  
  puts("# testing vector:");
  
  v = allocate_vector(3);

  bltn_vector_set_bang(v, SDNUMB(0), SDNUMB(101));
  bltn_vector_set_bang(v, SDNUMB(1), SDNUMB(102));
  bltn_vector_set_bang(v, SDNUMB(2), SDNUMB(103));

  n = bltn_vector_ref(v, SDNUMB(0));
  assert(n.tag == tnumb);
  printf("%ld\n", n.data.ivalue);
  
  n = bltn_vector_ref(v, SDNUMB(1));
  assert(n.tag == tnumb);
  printf("%ld\n", n.data.ivalue);
  
  n = bltn_vector_ref(v, SDNUMB(2));
  assert(n.tag == tnumb);
  printf("%ld\n", n.data.ivalue);

  puts("");
  puts("");
}

void test_eq() {
  sdata b;
  
  puts("# testing eq:");
  
  b = bltn_eq_question(SDBOOL(0), SDBOOL(1));
  assert(b.tag == tbool);
  printf("0 %ld\n", b.data.value);
  
  b = bltn_eq_question(SDBOOL(0), SDBOOL(0));
  assert(b.tag == tbool);
  printf("1 %ld\n", b.data.value);
  
  b = bltn_eq_question(SDBOOL(1), SDBOOL(1));
  assert(b.tag == tbool);
  printf("1 %ld\n", b.data.value);
  
  b = bltn_eq_question(SDBOOL(0), SDNUMB(1));
  assert(b.tag == tbool);
  printf("0 %ld\n", b.data.value);
  
  b = bltn_eq_question(SDBOOL(1), SDNUMB(1));
  assert(b.tag == tbool);
  printf("0 %ld\n", b.data.value);
  
  b = bltn_eq_question(SDNUMB(5), SDNUMB(5));
  assert(b.tag == tbool);
  printf("1 %ld\n", b.data.value);
  
  b = bltn_eq_question(SDNUMB(5), SDNUMB(8));
  assert(b.tag == tbool);
  printf("0 %ld\n", b.data.value);

  puts("");
  puts("");
}

void test_dispatch() {
  u label;
  sdata n;
  
  puts("# testing dispatch:");

  label = 0;

  while (1) {
    switch (label) {
    case 0:
      stack_push((sdata){.tag = tlabl, .data.ivalue = 1});
      label = 2;
      break;
      
    case 1:
      n = stack_pop();
      assert(n.tag == tnumb);
      label = n.data.value;
      printf("%ld\n", n.data.value);
      
      puts("");
      puts("");
      return;
      break;
      
    case 2:
      n = stack_pop();
      assert(n.tag == tlabl);
      label = n.data.value;
      stack_push((sdata){.tag = tnumb, .data.ivalue = 7});
      break;
    }
  }
}

int main(void) {
  printf("word size: %lu bits.\n", sizeof(u)*8);
  puts("");

  test_symbol_table();
  test_stack();
  test_cons();
  test_cons2();
  test_vector();
  test_eq();
  test_dispatch();

  test_stack_fold();
  
  return 0;
}
